#ifndef CHAT_SERVER_H
#define CHAT_SERVER_H

#include <gtkmm.h>
#include <libglademm.h>

#include <splash_screen.h>
#include <main_window.h>
#include <user_database.h>
#include <tcp_server.h>

class MalformedRequest:public std::exception{
	public:
		MalformedRequest(const std::string& s)throw():s(s){}
		~MalformedRequest()throw(){}
		const char* what()throw(){
			return ("Malformed request:"+s).c_str();
		}
	private:
		std::string s;
};

class ChatServer:public sigc::trackable{
	public:
		ChatServer():kit(*Gtk::Main::instance())/*main_loop_ (Glib::MainLoop::create())*/{
			kit.signal_run().connect(sigc::mem_fun(*this, &ChatServer::launch_threads));
			//launch_threads();
		}
		virtual ~ChatServer(){}
		void run(){
			g_debug("ChatServer::run()");
			kit.run();
		}
		
		void launch_threads(){
			splash_screen=new SplashScreen();
			g_debug("ChatServer::launch_threads()");
			//spawn a thread to pull data from database
			Glib::Thread* cacheFromDatabase_thread=Glib::Thread::create(sigc::mem_fun(*this, &ChatServer::cacheFromDatabase), true);
			//spawn a thread for loading the main window_widget
			Glib::Thread* loadMainWindow_thread=Glib::Thread::create(sigc::mem_fun(*this, &ChatServer::loadMainWindow), true);
			//when both threads have joined, destroy the splash screen
			cacheFromDatabase_thread->join();
			loadMainWindow_thread->join();
			g_debug("ChatServer::launch_threads-finished");
			/*Glib::Thread* acceptIncomingConnections_thread=*/Glib::Thread::create(sigc::mem_fun(*this, &ChatServer::acceptIncomingConnections), true);
			/*Glib::Thread* updateUserStatus_thread=*/Glib::Thread::create(sigc::mem_fun(*this, &ChatServer::updateUserStatuses), true);
			delete splash_screen;
		}
	private:
		void cacheFromDatabase(){
			splash_screen->setInfoText("Connecting to database...");
			try{
				user_database=new UserDatabase("naaaaak.com");
				std::vector<const User*> users=user_database->getUsers();
				//populate main window with users
				for(std::vector<const User*>::const_iterator itr=users.begin(); itr!=users.end(); ++itr){
					main_window->addUser(**itr);
					main_window->updateUserStatuses(users);
				}
			}
			catch(UnableToConnect& x){
				g_warning("ChatServer::cacheFromDatabase():%s", x.what());
				throw;
			}
			catch(UnableToSend& x){
				g_warning("ChatServer::cacheFromDatabase():%s", x.what());
				throw;
			}
			
			//parse resonse and cache data
			splash_screen->setInfoText("Creating data cache...");
			
			throw Glib::Thread::Exit();
		}
		void loadMainWindow(){
			g_debug("ChatServer::loadMainWindow");
			//create a MainWindow object and start its execution
			splash_screen->setInfoText("Creating MainWindow...");
			
			main_window=new MainWindow();
			
			throw Glib::Thread::Exit();
		}
		void acceptIncomingConnections(){
			TcpServer connection(1138, sigc::mem_fun(*this, &ChatServer::handleIncomingConnection));
			while(1){
				connection.accept();
			}
		}
		void updateUserStatuses(){
			while(1){
				main_window->updateUserStatuses(user_database->getUsers());
				sleep(1);
			}
		}
		void handleIncomingConnection(TcpServer::Connection connection)try{
			std::string client_request, client_request1, client_request2;
			connection>>client_request;
			
			//Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
			//main_window->addMessage("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
			
			std::string response;
			response+="<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n";
			
			//parse client request
			
			TiXmlDocument document;
			document.Parse(client_request.c_str());
			TiXmlHandle docHandle( &document );
			
			TiXmlElement * message;
			TiXmlElement * request;
			//if there is a message element and (there is a request element or message_type==buddylist-request)
			if((message=document.FirstChildElement("message")) && 
				(request=message->FirstChildElement("request"))){
				std::string message_type=message->Attribute("type");
				if(message_type=="account-request"){
					
					Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					main_window->addMessage("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					//get email, username, and password hash
					
					std::string email;
					std::string username;
					std::string password_hash;
					
					try{
						if(request->Attribute("email"))
							email=request->Attribute("email");
						else
							throw MalformedRequest("email attribute not found");
						
						if(request->Attribute("username"))
							username=request->Attribute("username");
						else
							throw MalformedRequest("username attribute not found");
						
						if(request->Attribute("password"))
							password_hash=request->Attribute("password");
						else
							throw MalformedRequest("password attribute not found");
						
					
						user_database->addUser(email, username, password_hash);
						response+="<message type=\"account-response\">\n";
          				response+="<response value=\"success\" />\n";
       					response+="</message>\n";
					}
					catch(std::exception& x){
						g_warning("ChatServer::handleIncomingConnection()-account-request:%s", x.what());
						response+="<message type=\"account-response\">\n";
          				response+="<response value=\"fail\" message=\""+std::string(x.what())+"\"/>\n";
       					response+="</message>\n";
						
						Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-sent response:\n"+response);
						main_window->addMessage("ChatServer::handleIncomingConnection()-sent response:\n"+response);
					}
				}
				else if(message_type=="login-request"){
					
					Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					main_window->addMessage("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					//get email, password hash
					std::string email;
					std::string password_hash;
					try{
						if(request->Attribute("email"))
							email=request->Attribute("email");
						else
							throw MalformedRequest("email attribute not found");
						if(request->Attribute("password"))
							password_hash=request->Attribute("password");
						else
							throw MalformedRequest("password attribute not found");
						g_debug("Login Request: email:%s", email.c_str());
					
						user_database->setUserOnline(email, password_hash);
						main_window->updateUserStatus(user_database->getUser(email));
						response+="<message type=\"login-response\">\n";
          				response+="<response value=\"success\" />\n";
       					response+="</message>\n";
					}
					catch(std::exception& x){
						g_warning("ChatServer::handleIncomingConnection()-login-request:%s", x.what());
						response+="<message type=\"login-response\">\n";
          				response+="<response value=\"fail\" message=\""+std::string(x.what())+"\"/>\n";
       					response+="</message>\n";
						
						Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-sent response:\n"+response);
						main_window->addMessage("ChatServer::handleIncomingConnection()-sent response:\n"+response);
					}
					catch(...){
						response+="<message type=\"login-response\">\n";
          				response+="<response value=\"fail\" />\n";
       					response+="</message>\n";
					}
				}
				else if(message_type=="buddylist-request"){
					//get email
					std::string email;
					try{
						if(request->Attribute("email"))
							email=request->Attribute("email");
						else
							throw MalformedRequest("email attribute not found");
						
						
						response+="<message type=\"buddylist-resource\">\n";
						
						if(user_database->getUser(email).isOnline()){
							//get map of <username, status>
							std::map<std::string, std::string> status=user_database->getBuddyStatus(email);
	
							//send response
							for(std::map<std::string, std::string>::const_iterator itr=status.begin(); itr!=status.end(); ++itr){
								response+="<buddy name\""+itr->first+"\" ip=\""+connection.getAddress()+"\" status=\""+itr->second+"\"/>\n";
							}
						}
						else{
							response+="<response value=\"fail\" message=\"User Not Logged In\"/>\n";
						}
						response+="</message>\n";
					}
					catch(std::exception& x){
						g_warning("ChatServer::handleIncomingConnection()-account-request:%s", x.what());
						response+="<message type=\"account-response\">\n";
          				response+="<response value=\"fail\" message=\""+std::string(x.what())+"\"/>\n";
       					response+="</message>\n";
						
						Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-sent response:\n"+response);
						main_window->addMessage("ChatServer::handleIncomingConnection()-sent response:\n"+response);
					}
				}
				else if(message_type=="add-friend-request"){
					
					Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					main_window->addMessage("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					//get email, buddy email
					std::string email;
					std::string buddy_email;
					try{
						if(request->Attribute("email"))
							email=request->Attribute("email");
						else
							throw MalformedRequest("email attribute not found");
						
						if(request->Attribute("buddyemail"))
							buddy_email=request->Attribute("buddyemail");
						else
							throw MalformedRequest("buddyemail attribute not found");
						
						user_database->addBuddy(email, buddy_email);
						response+="<message type=\"add-friend-response\">\n";
          				response+="<response value=\"success\" />\n";
       					response+="</message>\n";
					}
					catch(std::exception& x){
						g_warning("ChatServer::handleIncomingConnection()-add-friend-request:%s", x.what());
						response+="<message type=\"add-friend-response\">\n";
          				response+="<response value=\"fail\" message=\""+std::string(x.what())+"\"/>\n";
       					response+="</message>\n";
						
						Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-sent response:\n"+response);
						main_window->addMessage("ChatServer::handleIncomingConnection()-sent response:\n"+response);
					}
					catch(...){
						response+="<message type=\"add-friend-response\">\n";
          				response+="<response value=\"fail\" />\n";
       					response+="</message>\n";
					}
				}
				else if(message_type=="remove-friend-request"){
					
					Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					main_window->addMessage("ChatServer::handleIncomingConnection()-got request:\n"+client_request);
					//get email, buddy email
					std::string email;
					std::string buddy_email;
					try{
						if(request->Attribute("email"))
							email=request->Attribute("email");
						else
							throw MalformedRequest("email attribute not found");
						
						if(request->Attribute("buddyemail"))
							buddy_email=request->Attribute("buddyemail");
						else
							throw MalformedRequest("buddyemail attribute not found");
						
						
						user_database->removeBuddy(email, buddy_email);
						
						response+="<message type=\"remove-friend-response\">\n";
          				response+="<response value=\"success\" />\n";
       					response+="</message>\n";
					}
					catch(std::exception& x){
						g_warning("ChatServer::handleIncomingConnection()-remove-friend-request:%s", x.what());
						response+="<message type=\"remove-friend-response\">\n";
          				response+="<response value=\"fail\" message=\""+std::string(x.what())+"\"/>\n";
       					response+="</message>\n";
						
						Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-sent response:\n"+response);
						main_window->addMessage("ChatServer::handleIncomingConnection()-sent response:\n"+response);
					}
					catch(...){
						response+="<message type=\"remove-friend-response\">\n";
          				response+="<response value=\"fail\" />\n";
       					response+="</message>\n";
					}
				}
				else if(message_type=="logout-request"){
					//get email
					std::string email;
					try{
						if(request->Attribute("email"))
							email=request->Attribute("email");
						else
							throw MalformedRequest("email attribute not found");
						
						user_database->setUserOffline(email);
						main_window->removeUser(user_database->getUser(email));
						response+="<message type=\"logout-response\">\n";
          				response+="<response value=\"success\" />\n";
       					response+="</message>\n";
					}
					catch(std::exception& x){
						g_warning("ChatServer::handleIncomingConnection()-logout-request:%s", x.what());
						response+="<message type=\"logout-response\">\n";
          				response+="<response value=\"fail\" message=\""+std::string(x.what())+"\"/>\n";
       					response+="</message>\n";
						
						Singleton<RemoteLog>::Instance().send("ChatServer::handleIncomingConnection()-sent response:\n"+response);
						main_window->addMessage("ChatServer::handleIncomingConnection()-sent response:\n"+response);
					}
					catch(...){
						response+="<message type=\"logout-response\">\n";
          				response+="<response value=\"fail\" />\n";
       					response+="</message>\n";
					}
				}
			}
			else{
				response+="<message type=\"unknown-response\">\n\t<response value=\"Unknown Request, Better luck next time\"/>\n";
			}//send response
			connection<<response;
			connection.close();
			throw Glib::Thread::Exit();
		}
		catch(std::exception& x){
			g_warning("Exception caught in ChatServer::handleIncomingConnection():%s", x.what());
		}
		Glib::RefPtr<Glib::MainLoop>  main_loop_;
		Gtk::Main kit;
		SplashScreen*  splash_screen;
		MainWindow*  main_window;
		UserDatabase* user_database;
};

#endif
